`timescale 1ns / 1ps
`include "top.vh"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2021/07/05 18:24:59
// Design Name: 
// Module Name: top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module top(reset, clk);
    input reset, clk;
    
    //---------------pc------------------
    wire pc_we, pc_we_uncond, pc_we_cond, pc_we_cond_satisfied, pc_source_sel;
    wire [`WORD_SIZE-1:0] pc_source, pc_out;
    
    //---------------mem-----------------
    wire mem_we, mem_adr_source_sel;
    wire [`WORD_SIZE-1:0] mem_din, mem_dout;
    wire [`MEM_ADR_LENGTH-1:0] mem_adr_source;
    reg [`WORD_SIZE-1:0] mem_data_reg;
    reg [`WORD_SIZE-1:0] inst_reg_store;
    reg [`WORD_SIZE-1:0] inst_reg;
    wire inst_reg_we;
    
    //---------------regfile-------------
    wire regfile_we;
    wire [1:0] regfile_din_source_sel;
    wire [`REGFILE_ADR_LENGTH-1:0] regfile_adr1, regfile_adr2, regfile_adr3;
    reg [`WORD_SIZE-1:0] regfile_din;
    wire [`WORD_SIZE-1:0] regfile_dout1, regfile_dout2;
    reg [`WORD_SIZE-1:0] regfile_dread_A, regfile_dread_B;
    
    //---------------alu-----------------
    wire [2:0] alu_control;
    reg [2:0] alu_sel;
    wire alu_opA_sel;
    wire [1:0] alu_opB_sel;
    wire [`WORD_SIZE-1:0] alu_opA_source;
    reg [`WORD_SIZE-1:0] alu_opB_source;
    wire [`WORD_SIZE-1:0] alu_dout;
    wire alu_out_zf; 
    reg [`WORD_SIZE-1:0] alu_result; 
    
    //---------------imm-generator-------
    wire [`ISA_IMM_LENGTH-1:0] extracted_imm;
    wire [`WORD_SIZE-1:0] sign_extended_imm;
    
    //---------------branch-related------
    wire branch_cond_sel;
    
    assign pc_source = (pc_source_sel)?alu_result:alu_dout;
    
    assign pc_we_cond_satisfied = (branch_cond_sel)?alu_dout[0]:alu_out_zf;
    assign pc_we = (pc_we_uncond | (pc_we_cond & pc_we_cond_satisfied));
    
    assign mem_adr_source = (mem_adr_source_sel)?alu_result:pc_out;
    assign mem_din = regfile_dread_B;
    
    always @(*)
    begin
        case (regfile_din_source_sel)
            2'b00: regfile_din = alu_result;
            2'b01: regfile_din = mem_data_reg;
            2'b10: regfile_din = sign_extended_imm;
            2'b11: regfile_din = 0;
        endcase
    end
    assign regfile_adr1 = inst_reg[9:5];
    assign regfile_adr2 = inst_reg[4:0];
    assign regfile_adr3 = inst_reg[14:10];
    
    always @(*)
    begin
        case (alu_control)
            3'b000: alu_sel = `ALU_OP_ADD;
            3'b001: alu_sel = `ALU_OP_SUB;
            3'b010: alu_sel = `ALU_OP_SLTU;
            3'b011: alu_sel = `ALU_OP_SLTS;
            default: alu_sel = inst_reg[28:26];
        endcase
    end

    assign alu_opA_source = (alu_opA_sel)?regfile_dread_A:pc_out;
    always @(*)
        case (alu_opB_sel)
            2'b00: alu_opB_source = regfile_dread_B;
            2'b01: alu_opB_source = 1;
            2'b10: alu_opB_source = sign_extended_imm;
            default: alu_opB_source = 0;
        endcase
    
    assign extracted_imm = {inst_reg[25:15], (inst_reg[31])?inst_reg[4:0]:inst_reg[14:10]};
    assign sign_extended_imm = {{16{extracted_imm[`ISA_IMM_LENGTH-1]}}, extracted_imm[`ISA_IMM_LENGTH-1:0]};
    
    always @(posedge clk)
    begin
        mem_data_reg <= mem_dout;
        if(inst_reg_we) inst_reg <= mem_dout;
        
        regfile_dread_A <= regfile_dout1;
        regfile_dread_B <= regfile_dout2;
        
        alu_result <= alu_dout;
    end
    
    latch #(.m(`WORD_SIZE)) program_counter (.din(pc_source), .clk(clk), .en(pc_we), 
                                                 .clr(reset), .dout(pc_out));
                                                 
    ram #(.m(`WORD_SIZE), .n(`MEM_ADR_LENGTH)) mem (.adr(mem_adr_source), .we(mem_we), .clk(clk), 
                                                    .din(mem_din), .dout(mem_dout));
                                                    
    regfile_2r1w #(.m(`WORD_SIZE), .n(`REGFILE_ADR_LENGTH)) regfile (.adr_rd_1(regfile_adr1), .adr_rd_2(regfile_adr2), 
                                                                     .adr_wr(regfile_adr3), .we(regfile_we), .clk(clk), 
                                                                     .din(regfile_din), .dout1(regfile_dout1), 
                                                                     .dout2(regfile_dout2));
                                                                     
    alu #(.m(`WORD_SIZE)) alu0 (.opA(alu_opA_source), .opB(alu_opB_source), .sel(alu_sel), .out(alu_dout), .zf(alu_out_zf));
    
    control_logic_fsm control (inst_reg[31:26], clk, 
                               pc_we_uncond, 
                               pc_we_cond,
                               pc_source_sel,
                               inst_reg_we,
                               mem_we, 
                               mem_adr_source_sel, 
                               regfile_we, 
                               regfile_din_source_sel,
                               alu_control,
                               alu_opA_sel,
                               alu_opB_sel, 
                               branch_cond_sel);
     
endmodule
